home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pico / attach.c < prev    next >
C/C++ Source or Header  |  1996-03-16  |  25KB  |  1,023 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: attach.c,v 4.33 1996/03/16 21:50:43 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Routines to support attachments in the Pine composer 
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  */
  30. #include <stdio.h>
  31. #include <ctype.h>
  32. #include <math.h>
  33. #include "osdep.h"
  34. #include "pico.h"
  35. #include "estruct.h"
  36. #include "edef.h"
  37. #include "efunc.h"
  38.  
  39. #ifdef    ATTACHMENTS
  40.  
  41.  
  42. #ifdef    ANSI
  43.     int    ParseAttach(struct hdr_line **,int *,char *,char *,char *,int *);
  44.     PATMT *NewAttach(char *, long, char *);
  45.     void   ZotAttach(struct pico_atmt *);
  46.     int    sinserts(char *, int, char *, int);
  47.     int       AttachUpload(char *, char *);
  48.     int       AttachCancel(char *);
  49. #else
  50.     int    ParseAttach();
  51.     PATMT *NewAttach();
  52.     void   ZotAttach();
  53.     int    sinserts();
  54.     int       AttachUpload();
  55.     int       AttachCancel();
  56. #endif
  57.  
  58.  
  59. /* 
  60.  * max number of attachments
  61.  */
  62. #define    MAXATCH    64
  63. #define    HIBIT_WARN    "Only ASCII characters allowed in attachment comments"
  64.  
  65.  
  66. /*
  67.  * AskAttach - ask for attachment fields and build resulting structure
  68.  *             return pointer to that struct if OK, NULL otherwise
  69.  */
  70. AskAttach(fn, sz, cmnt)
  71. char *fn, *sz, *cmnt;
  72. {
  73.     int        i, status, upload = 0;
  74.     long    l = 0;
  75.     char    bfn[NFILEN];
  76.  
  77.     i = 2;
  78.     fn[0] = '\0';
  79.     sz[0] = '\0';
  80.     cmnt[0] = '\0';
  81.  
  82.     while(i){
  83.     if(i == 2){
  84.         EXTRAKEYS menu_attach[3];
  85.         int          n;
  86.  
  87.         menu_attach[n = 0].name  = "^T";
  88.         menu_attach[n].label     = "To Files";
  89.         menu_attach[n].key         = (CTRL|'T');
  90. #if    !defined(DOS) && !defined(MAC)
  91.         if(Pmaster && Pmaster->upload){
  92.         /*
  93.          * The Plan: ^R prompts for uploaded file's name which
  94.          * is passed to the defined upload command when the user
  95.          * hits Return to confirm the name.
  96.          * NOTE: this is different than upload into message
  97.          * text in which case the uploaded name isn't useful so
  98.          * a temp file is ok (be sure to fix the file mode).
  99.          */
  100.         menu_attach[++n].name = "^Y";
  101.         menu_attach[n].key    = (CTRL|'Y');
  102.         menu_attach[n].label  = upload ? "Read File" : "RcvUpload";
  103.         }
  104. #endif
  105.  
  106.         menu_attach[++n].name  = NULL;
  107.         KS_OSDATASET(&menu_attach[0], KS_NONE);
  108.         status = mlreply(upload ? "Name to give uploaded attachment: "
  109.                     : "File to attach: ",
  110.                  fn, NLINE, QNORML, menu_attach);
  111.     }
  112.     else
  113.       status = mlreply("Attachment comment: ", cmnt, NLINE, QNORML, NULL);
  114.  
  115.     switch(status){
  116.       case HELPCH:
  117.         emlwrite("No Attachment %s help yet!",
  118.              (i == 2) ? "file" : "comment");
  119. /* remove break and sleep when help text done to force redraw */     
  120.         sleep(3);
  121.         break;
  122.  
  123.       case (CTRL|'T'):
  124.         if(i != 2){
  125.         (*term.t_beep)();
  126.         break;
  127.         }
  128.  
  129.         *bfn = '\0';
  130.         if(*fn == '\0' || !isdir(fn, NULL))
  131.           strcpy(fn, gmode&MDCURDIR ? "."
  132.                     : (gmode&MDTREE) ? opertree
  133.                              : gethomedir(NULL));
  134.  
  135.         if(FileBrowse(fn, bfn, sz, FB_READ) == 1){
  136.         strcat(fn, S_FILESEP);
  137.         strcat(fn, bfn);
  138.         if(upload && !AttachUpload(fn, sz)){
  139.             i = 2;            /* keep prompting for file */
  140.             sleep(3);            /* problem, show error! */
  141.         }
  142.         else
  143.           i--;                /* go prompt for comment */
  144.         }
  145.         else
  146.           *fn = '\0';
  147.  
  148.         /* fall thru to clean up the screen */
  149.  
  150.       case (CTRL|'L'):
  151.         refresh(FALSE, 1);
  152.         update();
  153.         continue;
  154.  
  155. #if    !defined(DOS) && !defined(MAC)
  156.       case (CTRL|'Y'):            /* upload? */
  157.         if(i == 2)
  158.           upload ^= 1;            /* flip mode */
  159.         else
  160.           (*term.t_beep)();
  161.  
  162.         break;
  163. #endif
  164.  
  165.       case ABORT:
  166.         return(AttachCancel((upload && i == 1) ? fn : NULL));
  167.  
  168.       case TRUE:                /* some comment */
  169.       case FALSE:                /* No comment */
  170.         if(i-- == 2){
  171.         if(upload){
  172.             fixpath(fn, NLINE);        /* names relative to ~ */
  173.             status = AttachUpload(fn, sz);
  174.             refresh(FALSE, 1);
  175.             update();
  176.             if(!status){
  177.             i = 2;            /* keep prompting for file */
  178.             sleep(3);        /* problem, show error! */
  179.             }
  180.         }
  181.         else if(fn[0]){
  182.             if(gmode&MDTREE)
  183.               compresspath(opertree, fn, NLINE);
  184.  
  185.             fixpath(fn, NLINE);        /* names relative to ~ */
  186.             if((gmode&MDTREE) && !in_oper_tree(fn)){
  187.             emlwrite(
  188.             "\007Restricted mode allows attachments from %s only",
  189.             (gmode&MDSCUR) ? "home directory" : opertree);
  190.             return(0);
  191.             }
  192.  
  193.             if((status=fexist(fn, "r", &l)) != FIOSUC){
  194.             fioperr(status, fn);    /* file DOESN'T exist! */
  195.             return(0);
  196.             }
  197.             strcpy(sz, prettysz(l));
  198.         }
  199.         else
  200.           return(AttachCancel((upload && i == 1) ? fn : NULL));
  201.         }
  202.         else{
  203.         mlerase();
  204.         return(1);            /* mission accomplished! */
  205.         }
  206.  
  207.         break;
  208.       default:
  209.         break;
  210.     }
  211.     }
  212. }
  213.  
  214.  
  215. /*
  216.  * AttachUpload - Use call back to run the external upload command.
  217.  */
  218. int
  219. AttachUpload(fn, sz)
  220.     char *fn, *sz;
  221. {
  222.     long l;
  223.  
  224.     if(gmode&MDSCUR){
  225.     emlwrite("\007Restricted mode disallows uploaded command", NULL);
  226.     return(0);
  227.     }
  228.  
  229.     if(Pmaster && Pmaster->upload && (*Pmaster->upload)(fn, &l)){
  230.     strcpy(sz, prettysz(l));
  231.     return(1);
  232.     }
  233.  
  234.     return(0);
  235. }
  236.  
  237.  
  238. /*
  239.  * AttachCancel - 
  240.  */
  241. int
  242. AttachCancel(fn)
  243.     char *fn;
  244. {
  245.     emlwrite("Attach cancelled", NULL);
  246.     if(fn && fn[0])
  247.       unlink(fn);                /* blast uploaded file */
  248.  
  249.     return(0);
  250. }
  251.  
  252.  
  253. extern struct headerentry *headents;
  254.  
  255. /*
  256.  * SyncAttach - given a pointer to a linked list of attachment structures,
  257.  *              return with that structure sync'd with what's displayed.
  258.  *              delete any attachments in list of structs that's not on 
  259.  *              the display, and add any that aren't in list but on display.
  260.  */
  261. SyncAttach()
  262. {
  263.     int offset = 0,                /* the offset to begin       */
  264.         rv = 0,
  265.         ki = 0,                    /* number of known attmnts   */
  266.         bi = 0,                    /* build array index         */
  267.         na,                    /* old number of attachmnt   */
  268.         status, i, j, n;
  269.     char file[NLINE],                /* buffers to hold it all    */
  270.          size[32],
  271.          comment[1024];
  272.     struct hdr_line *lp;            /* current line in header    */
  273.     struct headerentry *entry;
  274.     PATMT *tp, *knwn[MAXATCH], *bld[MAXATCH];
  275.  
  276.     for(entry = headents; entry->name != NULL; entry++) {
  277.       if(entry->is_attach)
  278.     break;
  279.     }
  280.  
  281.     if(Pmaster == NULL)
  282.       return(-1);
  283.  
  284.     for(i=0;i<MAXATCH;i++)            /* bug - ever pop this? */
  285.     knwn[i] = bld[i] = NULL;        /* zero out table */
  286.     
  287.     tp = Pmaster->attachments;
  288.     while(tp != NULL){                /* fill table of     */
  289.     knwn[ki++] = tp;            /* known attachments */
  290.     tp = tp->next;
  291.     }
  292.  
  293.     n = 0;
  294.  
  295.     lp = entry->hd_text;
  296.     while(lp != NULL){
  297.     na = ++n;
  298.  
  299.     if(status = ParseAttach(&lp, &offset, file, size, comment, &na))
  300.         rv = (rv < 0) ? rv : status ;    /* remember worst case */
  301.  
  302.     if(*file == '\0'){
  303.         if(n != na && na > 0 && na <= ki && (knwn[na-1]->flags & A_FLIT)){
  304.         bld[bi++] = knwn[na-1];
  305.         knwn[na-1] = NULL;
  306.         }
  307.         continue;
  308.     }
  309.  
  310.     if((gmode&MDTREE) && !in_oper_tree(file))
  311.       /* no attachments outsize ~ in secure mode! */
  312.       continue;
  313.  
  314.     tp = NULL;
  315.     for(i = 0; i < ki; i++){        /* already know about it? */
  316.         /*
  317.          * this is kind of gruesome. what we want to do is keep track
  318.          * of literal attachment entries because they may not be 
  319.          * actual files we can access or that the user can readily 
  320.          * access.
  321.          */
  322.         if(knwn[i]
  323.            && ((!(knwn[i]->flags&A_FLIT)
  324.             && !strcmp(file, knwn[i]->filename))
  325.            || ((knwn[i]->flags&A_FLIT) && i+1 == na))){
  326.         tp = knwn[i];
  327.         knwn[i] = NULL;            /* forget we know about it */
  328.  
  329.         if(status == -1)        /* ignore garbage! */
  330.           break;
  331.  
  332.         if((tp->flags&A_FLIT) && strcmp(file, tp->filename)){
  333.             rv = 1;
  334.             if((j=strlen(file)) > strlen(tp->filename)){
  335.             if((tp->filename = (char *)realloc(tp->filename,
  336.                             sizeof(char)*(j+1))) == NULL){
  337.                 emlwrite("\007Can't realloc filename space",NULL);
  338.                 return(-1);
  339.             }
  340.             }
  341.  
  342.             strcpy(tp->filename, file);
  343.         }
  344.         else if(tp->size && strcmp(tp->size, size)){
  345.             rv = 1;
  346.             if((j=strlen(size)) > strlen(tp->size)){
  347.             if((tp->size=(char *)realloc(tp->size,
  348.                             sizeof(char)*(j+1))) == NULL){
  349.                 emlwrite("\007Can't realloc space for size", NULL);
  350.                 return(-1);
  351.             }
  352.             }
  353.  
  354.             strcpy(tp->size, size);
  355.         }
  356.  
  357.         if(strcmp(tp->description, comment)){    /* new comment */
  358.             rv = 1;
  359.             if((j=strlen(comment)) > strlen(tp->description)){
  360.             if((tp->description=(char *)realloc(tp->description,
  361.                         sizeof(char)*(j+1))) == NULL){
  362.                 emlwrite("\007Can't realloc description", NULL);
  363.                 return(-1);
  364.             }
  365.             }
  366.               
  367.             strcpy(tp->description, comment);
  368.         }
  369.         break;
  370.         }
  371.     }
  372.  
  373.     if(tp){
  374.         bld[bi++] = tp;
  375.     }
  376.     else{
  377.         if(file[0] != '['){
  378.         if((tp = NewAttach(file, atol(size), comment)) == NULL)
  379.           return(-1);
  380.         bld[bi++] = tp;
  381.         }
  382.         else break;
  383.     }
  384.  
  385.     if(status < 0)
  386.       tp->flags |= A_ERR;        /* turn ON error bit */
  387.     else
  388.       tp->flags &= ~(A_ERR);    /* turn OFF error bit */
  389.     }
  390.  
  391.     for(i=0; i < bi; i++)        /* link together newly built list */
  392.     bld[i]->next = bld[i+1];
  393.  
  394.     Pmaster->attachments = bld[0];
  395.  
  396.     for(i = 0; i < ki; i++){        /* kill old/unused references */
  397.  
  398.     if(knwn[i]){
  399.         ZotAttach(knwn[i]);
  400.         free((char *) knwn[i]);
  401.     }
  402.     }
  403.  
  404.     return(rv);
  405. }
  406.  
  407.  
  408.  
  409.  
  410. /*
  411.  * ParseAttach - given a header line and an offset into it, return with 
  412.  *         the three given fields filled in.  Assumes the size of 
  413.  *         the buffers passed is large enough to hold what's there.
  414.  *         Always updates header fields that have changed or are 
  415.  *         fixed.  An error advances offset to next attachment.
  416.  *
  417.  *        returns: 1 if a field changed
  418.  *                       0 nothing changed
  419.  *                      -1 on error
  420.  */
  421. ParseAttach(lp, off, fn, sz, cmnt, no)
  422. struct hdr_line **lp;                /* current header line      */
  423. int  *off;                    /* offset into that line    */
  424. char *fn, *sz, *cmnt;                /* places to return fields  */
  425. int  *no;                    /* attachment number        */
  426. {
  427.     int  j, status,                /* various return codes     */
  428.          rv = 0,                /* return value             */
  429.      orig_offset,
  430.          lbln  = 0,                /* label'd attachment        */
  431.      hibit = 0;
  432.     long l;                    /* attachment length        */
  433.     char tmp[1024],
  434.      c,
  435.      c_lookahead,
  436.         *p,
  437.         *lblsz = NULL,                /* label'd attchmnt's size  */
  438.          number[8];
  439.     register struct hdr_line  *lprev;
  440.     enum {                    /* parse levels             */
  441.     LWS,                    /* leading white space      */
  442.     NUMB,                    /* attachment number        */
  443.     WSN,                    /* white space after number */
  444.     TAG,                    /* attachments tag (fname)  */
  445.     WST,                    /* white space after tag    */
  446.     SIZE,                    /* attachments size         */
  447.     SWS,                    /* white space after size   */
  448.     CMMNT,                    /* attachment comment       */
  449.     TG} level;                /* trailing garbage         */
  450.  
  451.     *fn = *sz = *cmnt = '\0';            /* initialize return strings */
  452.     p   = tmp;
  453.     orig_offset = *off;
  454.  
  455.     level = LWS;                /* start at beginning */
  456.     while(*lp != NULL){
  457.  
  458.     if((c=(*lp)->text[*off]) == '\0'){    /* end of display line */
  459.         int lwsp = 0;
  460.  
  461.         while(isspace((*lp)->text[lwsp]))
  462.           lwsp++;
  463.  
  464.         if(lwsp){                /* blat leading whitespace  */
  465.         *off -= lwsp;
  466.         for(j = 0; (*lp)->text[j] = (*lp)->text[j+lwsp]; j++)
  467.           ;
  468.         }
  469.  
  470.         while(*off && isspace((*lp)->text[*off - 1]))
  471.           (*lp)->text[--(*off)] = '\0';    /* blat trailing whitespace */
  472.  
  473.         lprev = *lp;
  474.         if((*lp = (*lp)->next) != NULL)
  475.           c = (*lp)->text[*off = 0];    /* reset offset */
  476.     }
  477.  
  478.     if(c != '\0')
  479.       c_lookahead = (*lp)->text[*off + 1];
  480.  
  481.     switch(level){
  482.       case LWS:                /* skip leading white space */
  483.         if(isspace(c) || c == '\0'){
  484.         break;
  485.         } else if(c == ','){
  486.         c = (*lp)->text[*off] = ' ';    /* rub out stray comma */
  487.         break;
  488.         }
  489.         else if(!isdigit(c)){        /* add a number */
  490.         sprintf(number, "%d. ", *no);
  491.         *no = 0;            /* no previous number! */
  492.         sinserts((*lp == NULL) ? &lprev->text[*off]
  493.                            : &(*lp)->text[*off],
  494.                  0, number, j=strlen(number));
  495.         *off += j - 1;
  496.         rv = 1;
  497.         level = TAG;            /* interpret the name */
  498.         break;
  499.         }
  500.         level = NUMB;
  501.       case NUMB:                /* attachment number */
  502.         if(c == '\0' || c == ','){        /* got to end, no number yet */
  503.         *p = '\0';
  504.         sprintf(number, "%d. ", *no);
  505.         *no = 0;            /* no previous number! */
  506.         if(c == '\0')
  507.           *lp = lprev;            /* go back and look at prev */
  508.  
  509.         c = (*lp)->text[*off = orig_offset];    /* reset offset */
  510.         sinserts((*lp == NULL) ? &lprev->text[*off]
  511.                            : &(*lp)->text[*off],
  512.                  0, number, j=strlen(number));
  513.         *off += j - 1;
  514.         rv = 1;
  515.         p = tmp;
  516.         level = WSN;            /* what's next... */
  517.         break;
  518.         }
  519.         else if(c == '.'            /* finished grabbing number */
  520.            && isspace(c_lookahead)){    /* if not space is not number */
  521.         /*
  522.          * replace number if it's not right
  523.          */
  524.         *p = '\0';
  525.         sprintf(number, "%d", *no);    /* record the current...  */
  526.         *no = atoi(tmp);        /* and the old place in list */
  527.         if(strcmp(number, tmp)){
  528.             if(p-tmp > *off){        /* where to begin replacemnt */
  529.             j = (p-tmp) - *off;
  530.             sinserts((*lp)->text, *off, "", 0);
  531.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  532.                  number, strlen(number));
  533.             *off = 0;
  534.             }
  535.             else{
  536.             j = (*off) - (p-tmp);
  537.             sinserts((*lp == NULL) ? &lprev->text[j] 
  538.                                : &(*lp)->text[j], 
  539.                  p-tmp , number, strlen(number));
  540.             *off += strlen(number) - (p-tmp);
  541.             }
  542.             rv = 1;
  543.         }
  544.  
  545.         p = tmp;
  546.         level = WSN;            /* what's next... */
  547.         }
  548.         else if(c < '0' || c > '9'){    /* Must be part of tag */
  549.         sprintf(number, "%d. ", *no);
  550.         sinserts((*lp == NULL) ? &lprev->text[(*off) - (p - tmp)]
  551.                            : &(*lp)->text[(*off) - (p - tmp)],
  552.                  0, number, j=strlen(number));
  553.         *off += j;
  554.         level = TAG;            /* interpret the name */
  555.         goto process_tag;    /* in case already past end of tag */
  556.         }
  557.         else
  558.           *p++ = c;
  559.  
  560.         break;
  561.  
  562.       case WSN:                /* blast whitespace */
  563.         if(isspace(c) || c == '\0'){
  564.         break;
  565.         }
  566.         else if(c == '['){            /* labeled attachment */
  567.         lbln++;
  568.         }
  569.         else if(c == ',' || c == ' '){
  570.         emlwrite("\007Attchmnt: '%c' not allowed in file name", 
  571.               (void *)(int)c);
  572.         rv = -1;
  573.         level = TG;            /* eat rest of garbage */
  574.         break;
  575.         }
  576.         level = TAG;
  577.       case TAG:                /* get and check filename */
  578.                         /* or labeled attachment  */
  579. process_tag:                    /* enclosed in []         */
  580.         if(c == '\0' || (!lbln && (isspace(c) || strchr(",(\"", c)))
  581.            || (lbln && c == ']')){
  582.         if(p != tmp){
  583.             *p = '\0';            /* got something */
  584.  
  585.             strcpy(fn, tmp);        /* store file name */
  586.             if(!lbln){            /* normal file attachment */
  587.             if(gmode&MDTREE)
  588.               compresspath(opertree, fn, NLINE);
  589.  
  590.             fixpath(fn, NLINE);
  591.             if((status=fexist(fn, "r", &l)) != FIOSUC){
  592.                 fioperr(status, fn);
  593.                 rv = -1;
  594.                 level = TG;        /* munch rest of garbage */
  595.                 break;
  596.             }
  597.  
  598.             if((gmode&MDTREE) && !in_oper_tree(fn)){
  599.                 emlwrite("\007Attachments allowed only from %s",
  600.                 (gmode&MDSCUR) ? "home directory" : opertree);
  601.                 rv = -1;
  602.                 level = TG;
  603.                 break;
  604.             }
  605.  
  606.             if(strcmp(fn, tmp)){     /* fn changed: display it */
  607.                 if(*off >=  p - tmp){    /* room for it? */
  608.                 sinserts((*lp == NULL)? 
  609.                      &lprev->text[(*off)-(p-tmp)] :
  610.                      &(*lp)->text[(*off)-(p-tmp)],
  611.                      p-tmp, fn, j=strlen(fn));
  612.                 *off += j - (p - tmp);    /* advance offset */
  613.                 rv = 1;
  614.                 }
  615.                 else{
  616.                 emlwrite("\007Attchmnt: Problem displaying real file path", NULL);
  617.                 }
  618.             }
  619.             }
  620.             else{            /* labelled attachment! */
  621.             /*
  622.              * should explain about labelled attachments:
  623.              * these are attachments that came into the composer
  624.              * with meaningless file names (up to caller of 
  625.              * composer to decide), for example, attachments
  626.              * being forwarded from another message.  here, we
  627.              * just make sure the size stays what was passed
  628.              * to us.  The user is SOL if they change the label
  629.              * since, as it is now, after changed, it will
  630.              * just get dropped from the list of what gets 
  631.              * passed back to the caller.
  632.              */
  633.             PATMT *tp;
  634.  
  635.             if(c != ']'){        /* legit label? */
  636.                 emlwrite("\007Attchmnt: Expected ']' after \"%s\"",
  637.                      fn);
  638.                 rv = -1;
  639.                 level = TG;
  640.                 break;
  641.             }
  642.             strcat(fn, "]");
  643.  
  644.             /*
  645.              * This is kind of cheating since otherwise
  646.              * ParseAttach doesn't know about the attachment
  647.              * struct.  OK if filename's not found as it will
  648.              * get taken care of later...
  649.              */
  650.             tp = Pmaster->attachments; /* caller check Pmaster! */
  651.             j = 0;
  652.             while(tp != NULL){
  653.                 if(++j == *no){
  654.                 lblsz = tp->size;
  655.                 break;
  656.                 }
  657.                 tp = tp->next;
  658.             }
  659.  
  660.             if(tp == NULL){
  661.                 emlwrite("\007Attchmnt: Unknown reference: %s",fn);
  662.                 lblsz =  "XXX";
  663.             }
  664.             }
  665.  
  666.             p = tmp;            /* reset p in tmp */
  667.             level = WST;
  668.         }
  669.  
  670.         if(!lbln && c == '(')        /* no space 'tween file, size*/
  671.           level = SIZE;
  672.         else if(c == '\0' || (!lbln && (c == ',' || c == '\"'))){
  673.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  674.             sprintf(tmp, " (%s) %s", sz, (c == '\"') ? "" : "\"\"");
  675.             sinserts((*lp == NULL) ? &lprev->text[*off] 
  676.                                : &(*lp)->text[*off],
  677.                  0, tmp, j = strlen(tmp));
  678.             *off += j;
  679.             rv = 1;
  680.             level = (c == '\"') ? CMMNT : TG;/* cmnt or eat trash */
  681.         }
  682.         }
  683.         else if(!lbln && (c == ',' || c == ' ' || c == '[' || c == ']')){
  684.         emlwrite("\007Attchmnt: '%c' not allowed in file name",
  685.               (void *)(int)c);
  686.         rv = -1;            /* bad char in file name */
  687.         level = TG;            /* gobble garbage */
  688.         }
  689.         else
  690.           *p++ = c;                /* add char to name */
  691.  
  692.         break;
  693.  
  694.       case WST:                /* skip white space */
  695.         if(!isspace(c)){
  696.         /*
  697.          * whole attachment, comment or done! 
  698.          */
  699.         if(c == ',' || c == '\0' || c == '\"'){
  700.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  701.             sprintf(tmp, " (%s) %s", sz, 
  702.                            (c == '\"') ? "" : "\"\"");
  703.             sinserts((*lp == NULL) ? &lprev->text[*off]
  704.                            : &(*lp)->text[*off],
  705.                  0, tmp, j = strlen(tmp));
  706.             *off += j;
  707.             rv = 1;
  708.             level = (c == '\"') ? CMMNT : TG;
  709.             lbln = 0;            /* reset flag */
  710.         }
  711.         else if(c == '('){        /* get the size */
  712.             level = SIZE;
  713.         }
  714.         else{
  715.             emlwrite("\007Attchmnt: Expected '(' or '\"' after %s",fn);
  716.             rv = -1;            /* bag it all */
  717.             level = TG;
  718.         }
  719.         }
  720.         break;
  721.  
  722.       case SIZE:                /* check size */
  723.         if(c == ')'){            /* finished grabbing size */
  724.         *p = '\0';
  725.         /*
  726.          * replace sizes if they don't match!
  727.          */
  728.         strcpy(sz, tmp);
  729.         if(strcmp(sz, (lblsz) ? lblsz : prettysz(l))){
  730.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  731.             if(p-tmp > *off){        /* where to begin replacemnt */
  732.             j = (p-tmp) - *off;
  733.             sinserts((*lp)->text, *off, "", 0);
  734.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  735.                  sz, strlen(sz));
  736.             *off = 0;
  737.             }
  738.             else{
  739.             j = (*off) - (p-tmp);
  740.             sinserts((*lp == NULL) ? &lprev->text[j]
  741.                                : &(*lp)->text[j],
  742.                  p-tmp , sz, strlen(sz));
  743.             *off += strlen(sz) - (p-tmp);
  744.             }
  745.             rv = 1;
  746.         }
  747.  
  748.         p = tmp;
  749.         level = SWS;            /* what's next... */
  750.         }
  751.         else if(c == '\0' || c == ','){
  752.         *p = '\0';
  753.         emlwrite("\007Attchmnt: Size field missing ')': \"%s\"", tmp);
  754.         rv = -1;
  755.         level = TG;
  756.         }
  757.         else
  758.           *p++ = c;
  759.  
  760.         break;
  761.  
  762.       case SWS:                /* skip white space */
  763.         if(!isspace(c)){
  764.         if(c == ','){            /* no description */
  765.             level = TG;            /* munch rest of garbage */
  766.             lbln = 0;            /* reset flag */
  767.         }
  768.         else if(c != '\"' && c != '\0'){
  769.             emlwrite("\007Attchmnt: Malformed comment, quotes required", NULL);
  770.             rv = -1;
  771.             level = TG;
  772.         }
  773.         else
  774.           level = CMMNT;
  775.         }
  776.         break;
  777.  
  778.       case CMMNT:                /* slurp up comment */
  779.         if(c == '\"' || c == '\0'){        /* got comment */
  780.         *p = '\0';            /* cap it off */
  781.         p = tmp;            /* reset p */
  782.         strcpy(cmnt,tmp);        /* copy the comment  */
  783.         if(c == '\0'){
  784.             emlwrite("\007Attchmnt: Closing quote required at end of comment", NULL);
  785.             rv = -1;
  786.         }
  787.         level = TG;            /* prepare for next one */
  788.         lbln = 0;            /* reset flag */
  789.         }
  790.         else
  791.           if(((*p++ = c) & 0x80) && !hibit++)
  792.         emlwrite(HIBIT_WARN, NULL);
  793.  
  794.         break;
  795.  
  796.       case TG:                /* get comma or final EOL */
  797.         if(!isspace(c)){
  798.         if(c != ',' && c != '\0'){
  799.             if(rv != -1)
  800.               emlwrite("\007Attchmnt: Comma must separate attachments", NULL);
  801.             rv = -1;
  802.         }
  803.         }
  804.         break;
  805.  
  806.       default:                /* something's very wrong */
  807.         emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL);
  808.         return(-1);                /* just give up */
  809.     }
  810.  
  811.     if(c == '\0')                /* we're done */
  812.       break;
  813.  
  814.     (*off)++;
  815.  
  816.     /*
  817.      * not in comment or label name? done. 
  818.      */
  819.     if(c == ',' && (level != CMMNT && !lbln))
  820.       break;                /* put offset past ',' */
  821.     }
  822.  
  823.     return(rv);
  824. }
  825.  
  826.  
  827.  
  828. /*
  829.  * NewAttach - given a filename (assumed to accessible) and comment, creat
  830.  */
  831. PATMT *NewAttach(f, l, c)
  832. char *f;
  833. long l;
  834. char *c;
  835. {
  836.     PATMT  *tp;
  837.  
  838.     if((tp=(PATMT *)malloc(sizeof(PATMT))) == NULL){
  839.     emlwrite("No memory to add attachment", NULL);
  840.     return(NULL);
  841.     }
  842.     else
  843.       memset(tp, 0, sizeof(PATMT));
  844.  
  845.     /* file and size malloc */
  846.     if((tp->filename = (char *)malloc(strlen(f)+1)) == NULL){
  847.     emlwrite("Can't malloc name for attachment", NULL);
  848.     free((char *) tp);
  849.     return(NULL);
  850.     }
  851.     strcpy(tp->filename, f);
  852.  
  853.     if(l > -1){
  854.     tp->size = (char *)malloc(sizeof(char)*(strlen(prettysz(l))+1));
  855.     if(tp->size == NULL){
  856.         emlwrite("Can't malloc size for attachment", NULL);
  857.         free((char *) tp->filename);
  858.         free((char *) tp);
  859.         return(NULL);
  860.     }
  861.     else
  862.       strcpy(tp->size, prettysz(l));
  863.     }
  864.  
  865.     /* description malloc */
  866.     if((tp->description = (char *)malloc(strlen(c)+1)) == NULL){
  867.     emlwrite("Can't malloc description for attachment", NULL);
  868.     free((char *) tp->size);
  869.     free((char *) tp->filename);
  870.     free((char *) tp);
  871.     return(NULL);
  872.     }
  873.     strcpy(tp->description, c);
  874.  
  875.     /* callback to show user the mime type that will be used for attachment */
  876.     if(Pmaster->mimetype  && (*Pmaster->mimetype)(f) > 0){
  877.     clearcursor();
  878.     mlerase();
  879.     (*Pmaster->showmsg)('x');
  880.     mpresf = 1;
  881.     }
  882.  
  883.     return(tp);
  884. }
  885.  
  886.  
  887.  
  888. /*
  889.  * AttachError - Sniff list of attachments, returning TRUE if there's
  890.  *               any sign of trouble...
  891.  */
  892. int
  893. AttachError()
  894. {
  895.     PATMT *ap;
  896.  
  897.     if(!Pmaster)
  898.       return(0);
  899.  
  900.     ap = Pmaster->attachments;
  901.     while(ap){
  902.     if((ap->flags) & A_ERR)
  903.       return(1);
  904.  
  905.     ap = ap->next;
  906.     }
  907.  
  908.     return(FALSE);
  909. }
  910.  
  911.  
  912.  
  913. void ZotAttach(p)
  914. PATMT *p;
  915. {
  916.     if(!p)
  917.       return;
  918.     if(p->description)
  919.       free((char *)p->description);
  920.     if(p->filename){
  921.     if(p->flags & A_TMP)
  922.       unlink(p->filename);
  923.  
  924.     free((char *)p->filename);
  925.     }
  926.     if(p->size)
  927.       free((char *)p->size);
  928.     if(p->id)
  929.       free((char *)p->id);
  930.     p->next = NULL;
  931. }
  932. #endif    /* ATTACHMENTS */
  933.  
  934.  
  935. /*
  936.  * intag - return TRUE if i is in a column that makes up an
  937.  *         attachment line number
  938.  */
  939. intag(s, i)
  940. char *s;
  941. int   i;
  942. {
  943.     char *p = s;
  944.     int n = 0;
  945.  
  946.     while(*p != '\0' && (p-s) < 5){        /* is there a tag? it */
  947.     if(n && *p == '.')            /* can't be more than 4 */
  948.       return(i <= p-s);                /* chars long! */
  949.  
  950.     if(*p < '0' || *p > '9')
  951.       break;
  952.     else
  953.       n = (n * 10) + (*p - '0');
  954.  
  955.     p++;
  956.     }
  957.  
  958.     return(FALSE);
  959. }
  960.  
  961.  
  962. /*
  963.  * prettysz - return pointer to string containing nice
  964.  */
  965. char *prettysz(l)
  966. long l;
  967. {
  968.     static char b[32];
  969.  
  970.     if(l < 1000)
  971.       sprintf(b, "%d  B", l);            /* xxx B */
  972.     else if(l < 10000)
  973.       sprintf(b, "%1.1f KB", (float)l/1000);    /* x.x KB */
  974.     else if(l < 1000000)
  975.       sprintf(b, "%d KB", l/1000);        /* xxx KB */
  976.     else if(l < 10000000)
  977.       sprintf(b, "%1.1f MB", (float)l/1000000); /* x.x MB */
  978.     else
  979.       sprintf(b, "%d MB", l/1000000);        /* xxx MB */
  980.     return(b);
  981. }
  982.  
  983.  
  984. /*
  985.  * sinserts - s insert into another string
  986.  */
  987. sinserts(ds, dl, ss, sl)
  988. char *ds, *ss;                    /* dest. and source strings */
  989. int  dl, sl;                    /* their lengths */
  990. {
  991.     char *dp, *edp;                /* pointers into dest. */
  992.     int  j;                    /* jump difference */
  993.  
  994.     if(sl >= dl){                /* source bigger than dest. */
  995.     dp = ds + dl;                /* shift dest. to make room */
  996.     if((edp = strchr(dp, '\0')) != NULL){
  997.         j = sl - dl;
  998.  
  999.         for( ;edp >= dp; edp--)
  1000.           edp[j] = *edp;
  1001.  
  1002.         while(sl--)
  1003.            *ds++ = *ss++;
  1004.     }
  1005.     else
  1006.       emlwrite("\007No end of line???", NULL);    /* can this happen? */
  1007.     }
  1008.     else{                    /* dest is longer, shrink it */
  1009.     j = dl - sl;                /* difference in lengths */
  1010.  
  1011.     while(sl--)                /* copy ss onto ds */
  1012.       *ds++ = *ss++;
  1013.  
  1014.     if(strlen(ds) > j){            /* shuffle the rest left */
  1015.         do
  1016.           *ds = ds[j];
  1017.         while(*ds++ != '\0');
  1018.     }
  1019.     else
  1020.       *ds = '\0';
  1021.     }
  1022. }
  1023.